package com.bowlong.net.proto.gen;

import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.nio.charset.Charset;
import java.util.List;

import com.bowlong.lang.PStr;
import com.bowlong.lang.StrEx;
import com.bowlong.tool.Lib2;
import com.bowlong.util.NewList;
import com.bowlong.util.NewMap;
import com.bowlong.util.StrBuilder;

@SuppressWarnings({ "unused", "resource" })
public class Bio2GJavaWeb extends Lib2 {
	public static void b2g(Class<?> c, boolean src, Class<?> ctx)
			throws Exception {
		B2Class B2C = c.getAnnotation(B2Class.class);
		String namespace = "";
		if (B2C != null) {
			namespace = B2C.namespace();
		}
		Class<?>[] classes = c.getDeclaredClasses();
		String p = (src ? "src/" : "") + "gen_b2g";
		// String p = "gen_b2g";

		String ctxClassName = ctx.getName();

		if (namespace != null && !namespace.isEmpty()) {
			p = p + "/" + namespace;
		}

		for (Class<?> class1 : classes) {
			String sname = class1.getSimpleName();
			if (B2G.isServer(class1)) {
				File path = new File(p);
				if (!path.exists())
					path.mkdirs();

				{
					StrBuilder sb = new StrBuilder();
					g2s_service(class1, namespace, ctxClassName, sb);
					writeFile(p + "/" + sname + ".java", sb.toString());
					System.out.println(sb);
				}
				// {
				// StrBuilder sb2 = new StrBuilder();
				// g2s_call(class1, namespace, sb2);
				// writeFile(p + "/" + "Call" + sname + ".java",
				// sb2.toString());
				// System.out.println(sb2);
				// }
			} else if (B2G.isData(class1)) {
				String p2 = p + "/bean";
				File path = new File(p2);
				if (!path.exists())
					path.mkdirs();

				String f = class1.getSimpleName();
				StrBuilder sb = new StrBuilder();
				if (B2G.isConstant(class1)) {
					g2beanConstant(class1, namespace, sb);
				} else {
					// g2bean(class1, namespace, sb);
				}
				writeFile(p2 + "/" + f + ".java", sb.toString());
				System.out.println(sb);
			}
		}

	}

	public static void g2beanConstant(Class<?> c, String namespace,
			StrBuilder sb) {
		Field[] fs = c.getDeclaredFields();
		String cname = c.getSimpleName();
		sb.pn("package gen_b2g${1}.bean;", StrEx.isEmpty(namespace) ? "" : "."
				+ namespace);
		sb.pn("");
		sb.pn("public class ${1} {", cname);
		for (Field field : fs) {
			B2Field a = field.getAnnotation(B2Field.class);
			String t = B2G.getType(field);
			String s = field.getName();
			if (s.contains("$"))
				continue;

			String remark = B2G.getRemark(field);
			String def = B2G.getDef(field);
			if ("".equals(def)) {
				continue;
			}
			if (field.getType().equals(List.class)) {
				continue;
			} else {
				if (t.contains("String")) {
					sb.pn("    public static final ${1} ${2} = \"${4}\"; ${3}",
							t, s, remark, def);

				} else {
					sb.pn("    public static final ${1} ${2} = ${4}; ${3}", t,
							s, remark, def);
				}
			}
		}
		sb.pn("}");
	}

	// 生成服务器接口
	public static void g2s_service(Class<?> c, String namespace,
			String ctxClassName, StrBuilder sb) {
		Method[] methods = c.getMethods();
		String cname = c.getSimpleName();
		sb.pn("package gen_b2g${1};", StrEx.isEmpty(namespace) ? "" : "."
				+ namespace);
		// sb.pn("package gen_b2g;");
		sb.pn("");
		sb.pn("import java.io.*;");
		sb.pn("import java.util.*;");
		sb.pn("");
		sb.pn("import com.bowlong.io.*;");
		sb.pn("import com.bowlong.util.*;");
		sb.pn("import com.bowlong.net.*;");
		sb.pn("");
		sb.pn("import ${1};", ctxClassName);
		sb.pn("");
		sb.pn("");
		sb.pn("@SuppressWarnings({  \"unchecked\", \"unused\", \"rawtypes\" })");
		sb.pn("public abstract class ${1} {", cname);
		sb.pn("");

		StrBuilder sb3 = new StrBuilder();
		StrBuilder sb4 = new StrBuilder();
		for (Method m : methods) {
			String rtype = B2G.getReturnType(m);
			if (B2G.isClient(m) && rtype.equals("void"))
				continue;
			String mname = B2G.getNethodName(m);
			int hmname = mname.hashCode();
			sb3.ap("$[1],", mname);
			if (B2G.isWriteOp(m))
				sb4.ap("$[1],", mname);
		}
		if (sb3.len() > 1)
			sb3.removeRight(1);
		if (sb4.len() > 1)
			sb4.removeRight(1);
		String sNames = sb3.toString();
		String sWrites = sb4.toString();

		sb.pn("    final Set<String> NAMES = NewSet.create(${1});", sNames);
		sb.pn("    final Set<Integer> WRITES = NewSet.create(${1});", sWrites);
		sb.pn("");
		sb.pn("    public boolean isWriteMethod(String cmd) {");
		sb.pn("        return WRITES.contains(cmd);");
		sb.pn("    }");
		sb.pn("");

		sb.pn("    public boolean in(String cmd) throws Exception {");
		sb.pn("        return NAMES.contains(cmd);");
		sb.pn("    }");
		sb.pn("");
		// sb.pn("    public abstract java.io.ByteArrayOutputStream getOutStream();");
		// sb.pn("    public abstract void freeOutStream(java.io.ByteArrayOutputStream baos);");
		// sb.pn("");

		sb.pn("    // //////////////////////////////////////////////");
		sb.pn("    // 逻辑分发");
		sb.pn("    // //////////////////////////////////////////////");
		sb.pn("");
		sb.pn("    public int disp(WebContext ctx) throws Exception {");
		sb.pn("        if(ctx == null) return 0;");
		sb.pn("        String cmd = ctx.func;");
		sb.pn("        return disp(ctx, cmd);");
		sb.pn("    }");
		sb.pn("    public int disp(WebContext ctx, String cmd) throws Exception {");
		sb.pn("        if(ctx == null) return 0;");
		sb.pn("        switch (cmd) {");
		for (Method m : methods) {
			String remark = B2G.getRemark(m);
			String srtype = B2G.getReturnType(m);
			String mname = B2G.getNethodName(m);
			int hmname = mname.hashCode();
			if (B2G.isServer(m)) {
				sb.pn("            case $[1]: { // ${2}", "/p/" + mname, remark);
				sb.pn("                return __${1}(ctx);", mname);
				sb.pn("            }");
			}
		}
		sb.pn("        }");
		sb.pn("        throw new Exception(\" cmd: \" + cmd + \":\" + ctx.params() + \" not found processor.\");");
		sb.pn("    }");
		sb.pn("");
		sb.pn("    // //////////////////////////////////////////////");
		sb.pn("    // 解析参数");
		sb.pn("    // //////////////////////////////////////////////");
		sb.pn("");
		for (Method m : methods) {
			String remark = B2G.getRemark(m);
			// String oType = B2G.getOType(m);
			String mname = B2G.getNethodName(m);
			String hmname = mname.hashCode() + "";
			boolean isJson = isJson(m);
			NewList<NewMap<String, String>> params = B2G.getParameters(m);

			StrBuilder sb1 = new StrBuilder();

			// 解析参数函数
			sb.pn("    // ${1}", remark);
			{
				sb.pn("    private int __${1}(WebContext ctx) throws Exception {",
						mname);
				sb.pn("        if(ctx == null) return 0;");
				StringBuffer sbThrow = new StringBuffer("Object[] othrows = {");

				int nOutNum = 0;
				for (NewMap<String, String> m1 : params) {
					String key = (String) m1.getKey();
					String val = (String) m1.getValue();
					String hval = val.hashCode() + "";
					String p = B2G.getMapType(key);
					boolean isOut = B2G.isOut(m, val);
					if (isOut) {
					} else {
						if (p.equals("getObject")) {
							sb.pn("        ${1} ${2} = ${1}.parse(map2.getNewMap(${3}));",
									key, val, hval);
						} else {

							if (p.equals("getList")) {
								String oType = B2G.getOType(m, val);
								String mType = B2G.getMapType(oType);
								if (mType.equals("getObject")) {
									sb.pn("        ${1}<${2}> ${3} = new NewList<${2}>();",
											key, oType, val);
									sb.pn("        {");
									sb.pn("            List<NewMap> maps = map2.${1}(${2});",
											p, hval);
									sb.pn("            for(NewMap m1 : maps) {");
									sb.pn("                ${1}.add(${2}.parse(m1));",
											val, oType);
									sb.pn("            }");
									sb.pn("        }");
									key = PStr.str("${1}<${2}>", key, oType);
								} else {
									sb.pn("        ${1} ${2} = ctx.${3}($[2]);",
											key, val, p);

								}
							} else {
								sb.pn("        ${1} ${2} = ctx.${3}($[2]);",
										key, val, p);
							}
						}
					}
					sb1.ap(", ${1}", val);

					sbThrow.append("\"\\\"").append(val).append(":\\\"\", ")
							.append(val).append(", ");
				}

				sbThrow.append("};");

				sb.pn("");
				sb.pn("        int len = 0;");
				if (isJson) {
					sb.pn("        Map rst = new HashMap();");
					sb.pn("        try {");
					sb.pn("            on${1}(ctx${2}, rst);", upper1(mname),
							sb1);
					sb.pn("            len = ctx.writeAndFlush(rst);");
					sb.pn("        } catch ( Exception e ) {");
					sb.pn("            ${1}", sbThrow);
					sb.pn("            onExcept(ctx, $[1], Log.rethrow2(e, $[1], othrows), rst);",
							mname);
					sb.pn("        }");
				} else {
					sb.pn("        StringBuffer rst = new StringBuffer();");
					sb.pn("        try {");
					sb.pn("            on${1}(ctx${2}, rst);", upper1(mname),
							sb1);
					sb.pn("            len = ctx.writeAndFlush(rst.toString());");
					sb.pn("        } catch ( Exception e ) {");
					sb.pn("            ${1}", sbThrow);
					sb.pn("            onExcept(ctx, $[1], Log.rethrow2(e, $[1], othrows), rst);",
							mname);
					sb.pn("        }");
				}
				sb.pn("        return len;");
				sb.pn("    }");

			}
		}

		sb.pn("");
		sb.pn("    public static Exception rethrow(Exception cause, String method, Object... params) {");
		sb.pn("        String causeMessage = cause.getMessage();");
		sb.pn("        if (causeMessage == null) {");
		sb.pn("            causeMessage = \"\";");
		sb.pn("        }");
		sb.pn("        StringBuffer msg = new StringBuffer(causeMessage);");
		sb.pn("        msg.append(\"\\r\\n\");");
		sb.pn("        msg.append(method);");
		sb.pn("        msg.append(\" Parameters: \");");
		sb.pn("        msg.append(\"\\r\\n\");");
		sb.pn("        if (params == null) {");
		sb.pn("            msg.append(\"[]\");");
		sb.pn("        } else {");
		sb.pn("            msg.append(Arrays.deepToString(params));");
		sb.pn("        }");
		sb.pn("        msg.append(\"\\r\\n\");");
		sb.pn("        Exception e = new Exception(msg.toString(), cause);");
		sb.pn("        return e;");
		sb.pn("    }");
		sb.pn("");
		sb.pn("");
		sb.pn("    // //////////////////////////////////////////////");
		sb.pn("    // 需要实现的接口");
		sb.pn("    // //////////////////////////////////////////////");
		sb.pn("");
		sb.pn("    public abstract void onExcept(WebContext ctx, String method, String except, StringBuffer ret) throws Exception;");
		sb.pn("    public abstract void onExcept(WebContext ctx, String method, String except, Map ret) throws Exception;");
		for (Method m : methods) {
			String remark = B2G.getRemark(m);
			// String oType = B2G.getOType(m);
			String mname = B2G.getNethodName(m);
			boolean isJson = isJson(m);
			NewList<NewMap<String, String>> params = B2G.getParameters(m);

			StrBuilder sb1 = new StrBuilder();
			for (NewMap<String, String> m1 : params) {
				String key = (String) m1.getKey();
				String val = (String) m1.getValue();
				String p = B2G.getMapType(key);
				if (p.equals("getList") && !"".equals(B2G.getOType(m, val))) {
					key = PStr.str("${1}<${2}>", key, B2G.getOType(m, val));
				}
				sb1.ap(", ${1} ${2}", key, val);
			}

			// 需要实现的逻辑函数
			if (B2G.isServer(m)) {
				sb.pn("    // ${1}", remark);
				if (isJson) {
					sb.pn("    public abstract void on${1}(WebContext ctx${2}, Map out) throws Exception;",
							upper1(mname), sb1);
				} else {
					sb.pn("    public abstract void on${1}(WebContext ctx${2}, StringBuffer out) throws Exception;",
							upper1(mname), sb1);
				}
			}
		}
		sb.pn("}");
	}

	public static void writeFile(String f, String str) {
		try (FileOutputStream out = new FileOutputStream(new File(f));
				OutputStreamWriter osw = new OutputStreamWriter(out,
						Charset.forName("UTF8"));) {
			osw.write(str, 0, str.length());
			osw.close();
			out.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public static String upper1(String s) {
		if (s == null || s.isEmpty())
			return s;
		int len = s.length();
		return s.substring(0, 1).toUpperCase() + s.substring(1, len);
	}

	public static final boolean isJson(Method m) {
		B2Method b2f = m.getAnnotation(B2Method.class);
		if (b2f == null)
			return false;
		boolean ret = (b2f.classify().equals("json"));
		return ret;
	}
}
